package com.simpou.commons.utils.lang;

import com.simpou.commons.utils.functional.Action;
import com.simpou.commons.utils.functional.Actions;
import java.util.Date;
import java.util.List;

/**
 * Auxilia nas operações que devem considerar elementos NULL.
 *
 * @author Jonas Pereira
 * @since 2012-10-22
 * @version 2013-07-29
 */
public class Nulls {

    /**
     * Testa se um objeto é não null, caso afirmativo retorna o mesmo abjeto,
     * caso contrário retorna um objeto alternativo.
     *
     * @param object Objeto a ser verificado.
     * @param alternative Objeto alternativo.
     * @return Objeto a ser verificado se não null ou o alternativo se null.
     */
    public static <T> T choose(final T object, final T alternative) {
        return (object == null) ? alternative : object;
    }

    /**
     * @param <T> Tipo do objeto a ser processado.
     * @param <E> Tipo do objeto de retorno ou resultado.
     * @param object Objeto a ser processado caso não null.
     * @param alternative Objeto a ser retornado caso o principal seja null.
     * @param action Ação a ser executada sobre o objeto principal caso não seja null.
     * @return Objeto principal processado pela ação. Caso este seja null é retornado o objeto alternativo.
     */
    public static <T, E> E execute(final T object, final E alternative, final Action<T, E> action) {
        final E result;
        if (object == null) {
            result = alternative;
        } else {
            result = Actions.execute(action, object);
        }
        return result;
    }

    /**
     * Consulta um item em um array.
     *
     * @param array Array.
     * @param position Posição do item no array.
     * @return Item se array é não nulo e posição existe ou null caso contrário.
     */
    public static <T> T get(final T[] array, final int position) {
        return ((position > -1) && (array != null)
                && (array.length > position)) ? array[position] : null;
    }

    /**
     * Consulta um item em uma lista.
     *
     * @param list Array.
     * @param position Posição do item na lista.
     * @return Item se lista é não nula e posição existe ou null caso contrário.
     */
    public static <T> T get(final List<T> list, final int position) {
        return ((position > -1) && (list != null) && (list.size() > position))
                ? list.get(position) : null;
    }

    /**
     * @param date Data base não null.
     * @param when Data a ser comparada com a base.
     * @param def Valor padrão em caso de null.
     * @return true se date for anterior when. Retorna def se when for null.
     * @throws NullPointerException Se date for null.
     */
    public static boolean before(final Date date, final Date when,
            final boolean def) {
        if (when == null) {
            return def;
        } else {
            return date.before(when);
        }
    }

    /**
     * @param date Data base não null.
     * @param when Data a ser comparada com a base.
     * @param def Valor padrão em caso de null.
     * @return true se date for posterior when. Retorna def se when for null.
     * @throws NullPointerException Se date for null.
     */
    public static boolean after(final Date date, final Date when,
            final boolean def) {
        if (when == null) {
            return def;
        } else {
            return date.after(when);
        }
    }

    /**
     * @param object Objeto.
     * @return Hashcode do objeto ou zero se null.
     */
    public static int hashCode(final Object object) {
        //TODO no futuro substituir pelo correspondente na Objects
        return (object == null) ? 0 : object.hashCode();
    }

    /**
     * @param object1 Objeto cujo método equals será invocado.
     * @param object2 Objeto alvo do equals do primeiro.
     * @return true se são iguais ou se ambos são null.
     */
    public static boolean equals(final Object object1, final Object object2) {
        if (object1 == null) {
            return object2 == null;
        } else {
            return object1.equals(object2);
        }
    }

    /**
     * @param array Array.
     * @return Clone do array ou null se array for null.
     */
    public static <T> T[] clone(final T[] array) {
        return (array == null) ? null : array.clone();
    }

    /**
     * @param date Date.
     * @return Clone do date ou null se date for null.
     */
    public static Date clone(final Date date) {
        return date == null ? null : (Date)date.clone();
    }
    
}
